﻿using Sustainalytics.AwsS3.Client;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.FileManager;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Utils;
using Sustainalytics.Utils.Configuration;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks.Dataflow;

namespace Sustainalytics.GCCS.CompanyReportSyncStep
{
    public class GenerateGccsPdfReportsSyncStep : ISyncStep
    {
        private readonly int _bulkSize;
        private readonly IS3Client _s3client;

        public GenerateGccsPdfReportsSyncStep()
        {
            _bulkSize = 500;

            _s3client = new S3Client(new S3ClientSettings()
            {
                AccessKey = SettingsHelper.Read("accesKeyId", "AKIAJKGZZMOFAVOVLHJA"),
                SecretAccessKey = SettingsHelper.Read("awsSecretKey", "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"),
                ServiceEndPoint = SettingsHelper.Read("s3ServiceEndPoint", "https://s3-eu-west-1.amazonaws.com/")
            });
        }

        public SyncStepOrder ExecutionOrder => SyncStepOrder.GenerateGccsPdfReports;

        public SyncContext Start(SyncContext syncContext)
        {
            // execute this step only if we have it checked
            var hasChanges = (syncContext.PreviousChanges.ToList()).Exists(x => x.AffectedKey == "Global Compact Documents Updated");
            if (!hasChanges)
            {
                syncContext.Log("No changes detected for global compact collections!");
                return syncContext;
            }

            string clientDwConnectionString;

            if (!syncContext.GAConnectionStrings.TryGetValue("ClientDW", out clientDwConnectionString))
            {
                throw new ArgumentException("ClientDW connection string is missing from SyncContext!");
            }

            var maxDegreeOfParallelism = ParallelHelper.GetMaxDegreeOfParallelism();

            var bulkDataReader = new GCCSReportBulkDataReader(_bulkSize, clientDwConnectionString,
                syncContext, syncContext.Token, maxDegreeOfParallelism);

            var currentQuarterRepo = new RepositoryCollection<GCCurrentQuarter>(clientDwConnectionString);
            var corporateDataRep = new RepositoryCollection<CorporateData>(clientDwConnectionString);

            var globalCompactResearchStates = corporateDataRep.ReadAllWhereFieldEqualsValue(x => x.GccsResearchState.ResearchState, ResearchState.ResearchAvailable, null, null);

            syncContext.Log($"Corporate data Has GCCS Researched companies: {globalCompactResearchStates.Count}");

            var imageFileManager = new DiskFileManager(@"Images\GcPdfExport");

            var gcExportManager = new GCExportManager(currentQuarterRepo, imageFileManager, null, null, null, null);

            var count = 0;

            Action<GccsReportData> action = reportData =>
            {
                if (reportData.OverallCompanySummary == null)
                {
                    syncContext.Log(string.Format("Company profile with Id {0} has OverallCompanySummary == null", reportData.CompanyId));
                    return;
                }

                if (reportData.Principles.Count > 0)
                {
                    var sw = Stopwatch.StartNew();

                    var gccsPdfReportContent = gcExportManager.GeneratePdfReport(reportData.CurrentQuarter, reportData.Assessment, false,
                        reportData.OverallCompanySummary, reportData.Principles, reportData.PrincipleEventIndicators, reportData.Sources);

                    var report = new GccsReport
                    {
                        Id = Guid.NewGuid(),
                        CompanyId = reportData.CompanyId,
                        Assessment = reportData.Assessment,
                        FileContent = gccsPdfReportContent,
                        MimeType = "application/pdf"
                    };

                    var companyName = reportData.OverallCompanySummary.CompanyProfile.Name;

                    report.FileName = reportData.Assessment.GetFullReportName(companyName, reportData.CompanyId);

                    S3Write(syncContext, report);

                    sw.Stop();

                    syncContext.Log($"{companyName} has report generated succesfully for assesment: {reportData.Assessment}. Company ID: {reportData.CompanyId}. Elapsed time:{sw.Elapsed}");
                    Interlocked.Increment(ref count);
                }
                else
                {
                    syncContext.Log(
                        $"{reportData.OverallCompanySummary.CompanyProfile.Name} has NO report generated for assesment: {reportData.Assessment}. Company ID: {reportData.CompanyId}");
                }
            };

            var gccsReportDataConsumer = new ActionBlock<GccsReportData>(action, new ExecutionDataflowBlockOptions
            {
                BoundedCapacity = maxDegreeOfParallelism * 10,
                CancellationToken = syncContext.Token,
                MaxDegreeOfParallelism = maxDegreeOfParallelism,
            });

            var gccsReportDataProducer = new BufferBlock<GccsReportData>(new DataflowBlockOptions
            {
                BoundedCapacity = maxDegreeOfParallelism * 10,
                CancellationToken = syncContext.Token
            });

            gccsReportDataProducer.LinkTo(gccsReportDataConsumer, new DataflowLinkOptions { PropagateCompletion = true });

            for (; ; )
            {
                syncContext.Token.ThrowIfCancellationRequested();

                try
                {
                    var batch = bulkDataReader.NextBulk();

                    if (!batch.Any())
                        break;

                    foreach (var reportData in batch)
                    {
                        syncContext.Token.ThrowIfCancellationRequested();

                        while (!gccsReportDataProducer.Post(reportData))
                        {
                            syncContext.Token.ThrowIfCancellationRequested();
                            Thread.Sleep(1000);
                        }
                    }
                }
                catch (Exception ex)
                {
                    syncContext.Log(ex.ToString());
                    if (syncContext.ErrorPolicy == ErrorPolicy.BreakRightAway)
                        throw;
                }
            }

            gccsReportDataProducer.Complete();
            gccsReportDataConsumer.Completion.Wait();

            syncContext.Log("Generated PDF's count:" + count);

            return syncContext;
        }

        private void S3Write(SyncContext syncContext, IFileContent report)
        {
            var bucketName = SettingsHelper.Read("s3bucket", "dev-globalaccess-europe");

            var path = $"{ReportType.GlobalCompactComplianceReport}/{report.FileName}";

            syncContext.Log($"Uploading file {path} in S3 {bucketName} bucket");

            _s3client.UploadFile(bucketName, path, report.FileContent);
        }
    }
}
